import "./styles/index.css";
import { NormalAI, PlayerType } from "./js/NormalAI";
import "./js/PPOAI";

/** 行 */
const ROWS: number = 15;
/** 列 */
const COLS: number = 15;

// 棋盘
const canvas: HTMLCanvasElement = document.getElementById("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
// 重置对局
const resetBtn: HTMLButtonElement = document.getElementById("resetBtn") as HTMLButtonElement;
// 人机对战
const PVEBtn: HTMLButtonElement = document.getElementById("PVEBtn") as HTMLButtonElement;
let isPVEBtnClick: boolean = false;
// 机机对战
const EVEBtn: HTMLButtonElement = document.getElementById("EVEBtn") as HTMLButtonElement;
let isEVEBtnClick: boolean = false;
// AI对战
const AIChange: HTMLButtonElement = document.getElementById("AIChange") as HTMLButtonElement;
// 训练AI
const trainAI: HTMLButtonElement = document.getElementById("trainAI") as HTMLButtonElement;
// 日志
const logEl: HTMLTextAreaElement = document.getElementById("log") as HTMLTextAreaElement;

/**
 * 编写画棋盘的函数，实现线条和交叉点的绘制
 */
function drawChessboard() {
    // 绘制背景颜色
    ctx.fillStyle = "#c19a6b";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.strokeStyle = "black";

    for (let i = 0; i < ROWS; i++) {
        ctx.beginPath();
        ctx.moveTo(15 + 30 * i, 15);
        ctx.lineTo(15 + 30 * i, 435);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(15, 15 + 30 * i);
        ctx.lineTo(435, 15 + 30 * i);
        ctx.stroke();
    }
}

/**
 * 实现画棋子的函数，在指定位置处绘制圆形棋子
 */
function drawChessPiece(row: number, col: number, color: string | CanvasGradient | CanvasPattern) {
    ctx.beginPath();
    ctx.arc(15 + col * 30, 15 + row * 30, 12, 0, 2 * Math.PI);
    ctx.fillStyle = color;
    ctx.fill();
}

/**
 * 实现判断胜负的函数，通过判断横向、纵向、左右对角线和右左对角线上是否存在连续的5个棋子来判断胜负
 */
function checkWin(row: number, col: number, board: number[][], player: number) {
    let count = 1;
    let i = row;
    let j = col;

    // 横向
    while (j - 1 >= 0 && board[i][j - 1] === player) {
        count++;
        j--;
    }
    j = col;
    while (j + 1 < COLS && board[i][j + 1] === player) {
        count++;
        j++;
    }
    if (count >= 5) return true;

    // 纵向
    count = 1;
    while (i - 1 >= 0 && board[i - 1][j] === player) {
        count++;
        i--;
    }
    i = row;
    while (i + 1 < ROWS && board[i + 1][j] === player) {
        count++;
        i++;
    }
    if (count >= 5) return true;

    // 左右对角线
    count = 1;
    while (i - 1 >= 0 && j - 1 >= 0 && board[i - 1][j - 1] === player) {
        count++;
        i--;
        j--;
    }
    i = row;
    j = col;
    while (i + 1 < ROWS && j + 1 < COLS && board[i + 1][j + 1] === player) {
        count++;
        i++;
        j++;
    }
    if (count >= 5) return true;

    // 右左对角线
    count = 1;
    while (i - 1 >= 0 && j + 1 < COLS && board[i - 1][j + 1] === player) {
        count++;
        i--;
        j++;
    }
    i = row;
    j = col;
    while (i + 1 < ROWS && j - 1 >= 0 && board[i + 1][j - 1] === player) {
        count++;
        i++;
        j--;
    }
    if (count >= 5) return true;

    return false;
}

// 初始化所有的赢法数组统计
function initWins(wins: Array<Array<Array<boolean>>>) {
    for (let i = 0; i < 15; ++i) {
        wins[i] = [];
        for (let j = 0; j < 15; ++j) wins[i][j] = [];
    }
    let count = 0; //赢法总数
    for (let i = 0; i < 15; ++i) {
        for (let j = 0; j < 11; j++) {
            for (let k = 0; k < 5; ++k) {
                wins[i][j + k][count] = true;
            }
            count++;
        }
    }
    for (let i = 0; i < 15; ++i) {
        for (let j = 0; j < 11; j++) {
            for (let k = 0; k < 5; ++k) {
                wins[j + k][i][count] = true;
            }
            count++;
        }
    }
    for (let i = 0; i < 11; ++i) {
        for (let j = 0; j < 11; j++) {
            for (let k = 0; k < 5; ++k) {
                wins[i + k][j + k][count] = true;
            }
            count++;
        }
    }
    for (let i = 0; i < 11; ++i) {
        for (let j = 14; j > 3; j--) {
            for (let k = 0; k < 5; ++k) {
                wins[i + k][j - k][count] = true;
            }
            count++;
        }
    }

    return count;
}

interface UpdateWin {
    i: number;
    j: number;
    wins: Array<Array<Array<boolean>>>;
    count: number;
    player1Win: number[];
    player2Win: number[];
    player: number;
}
function updateWin({ i, j, wins, count, player1Win, player2Win, player }: UpdateWin) {
    for (var k = 0; k < count; ++k) {
        if (wins[i][j][k]) {
            if (player === PlayerType.player1) {
                player1Win[k]++;
                player2Win[k] = 6;
            } else {
                player1Win[k] = 6;
                player2Win[k]++;
            }
        }
    }
}

function initBoard() {
    let wins: Array<Array<Array<boolean>>> = [];
    let count = initWins(wins);

    let player1Win: number[] = [];
    let player2Win: number[] = [];
    for (var i = 0; i < count; ++i) {
        player1Win[i] = 0;
        player2Win[i] = 0;
    }
    return { wins, count, player1Win, player2Win };
}

// 人机对战
PVEBtn.onclick = function () {
    PVEBtn.disabled = true;
    drawChessboard();

    /** 1代表自己 2代表另一个玩家 */
    let player: number = 1;
    // 游戏是否结束
    let gameOver: boolean = false;
    /** 棋盘数组 */
    const board = new Array(ROWS).fill(0).map(() => new Array(COLS).fill(0));

    let { wins, count, player1Win, player2Win } = initBoard();

    let normalAI = new NormalAI({ ROWS, COLS, player: 1, player1Win, player2Win, wins, count });

    canvas.onclick = function (e) {
        if (gameOver) return;
        const x = e.offsetX;
        const y = e.offsetY;
        const i = Math.floor(y / 30);
        const j = Math.floor(x / 30);
        if (board[i][j]) return; // 如果该位置已经有棋子则退出
        board[i][j] = player;
        drawChessPiece(i, j, player === 1 ? "black" : "white");
        updateWin({ i, j, wins, count, player1Win, player2Win, player });

        // 判断胜负
        if (checkWin(i, j, board, player)) {
            alert(`${player === 1 ? "黑方" : "白方"}胜利！`);
            gameOver = true;
            PVEBtn.disabled = false;
        } else {
            player = player === 1 ? 2 : 1; // 玩家轮流下棋
            let { u: row, v: col } = normalAI.computerAI(board);
            board[row][col] = player;
            drawChessPiece(row, col, player === 1 ? "black" : "white");
            updateWin({ i: row, j: col, wins, count, player1Win, player2Win, player });

            // 判断胜负
            if (checkWin(row, col, board, player)) {
                alert(`${player === 1 ? "黑方" : "白方"}胜利！`);
                gameOver = true;
                PVEBtn.disabled = false;
            } else {
                player = player === 1 ? 2 : 1; // 玩家轮流下棋
            }
        }
    };
};

function logInfo(info: string) {
    logEl.value += `${info}\n`;
}

// 机机对战
EVEBtn.onclick = function () {
    EVEBtn.disabled = true;

    drawChessboard();

    let { wins, count, player1Win, player2Win } = initBoard();

    let bot1 = new NormalAI({ ROWS, COLS, player: 1, player1Win, player2Win, wins, count });
    let bot2 = new NormalAI({ ROWS, COLS, player: 2, player1Win, player2Win, wins, count });

    const board = new Array(ROWS).fill(0).map(() => new Array(COLS).fill(0));
    let player: number = 1;

    let timer: NodeJS.Timer = setInterval(() => {
        const { u: row1, v: col1 } = bot1.computerAI(board);
        board[row1][col1] = bot1.player;
        player = bot1.player;
        drawChessPiece(row1, col1, player === 1 ? "black" : "white");
        logInfo(`${player === 1 ? "黑方" : "白方"}落子x:${row1}y:${col1}`);
        updateWin({ i: row1, j: col1, wins, count, player1Win, player2Win, player });

        if (checkWin(row1, col1, board, player)) {
            logInfo(`${player === 1 ? "黑方" : "白方"}胜利！`);
            clearInterval(timer);
            EVEBtn.disabled = false;
        } else {
            const { u: row2, v: col2 } = bot2.computerAI(board);
            board[row2][col2] = bot2.player;
            player = bot2.player;
            drawChessPiece(row2, col2, player === 1 ? "black" : "white");
            logInfo(`${player === 1 ? "黑方" : "白方"}落子x:${row2}y:${col2}`);
            updateWin({ i: row2, j: col2, wins, count, player1Win, player2Win, player });

            // 判断胜负
            if (checkWin(row2, col2, board, player)) {
                logInfo(`${player === 1 ? "黑方" : "白方"}胜利！`);
                clearInterval(timer);
                EVEBtn.disabled = false;
            }
        }
    }, 500);
};
